﻿using Microsoft.AspNetCore.Mvc;
using Surging.Core.ApiGateWay;
using Surging.Core.ApiGateWay.OAuth;
using Surging.Core.CPlatform;
using Surging.Core.CPlatform.Filters.Implementation;
using Surging.Core.CPlatform.Routing;
using Surging.Core.ProxyGenerator;
using Surging.Core.ProxyGenerator.Utilitys;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Linq;
using GateWayAppConfig = Surging.Core.ApiGateWay.AppConfig;
using System.Reflection;
using Gmmy.Utility;
using Gmmy.Utility.Enums;
using Newtonsoft.Json;
using Surging.Core.CPlatform.Utilities;
using Newtonsoft.Json.Linq;
using Surging.Core.Caching;
using Surging.Core.Common.Extensions;
using Surging.Core.CPlatform.Cache;

namespace ApiGateway.Controllers
{
    public class ServicesController : Controller
    {
        private readonly IServiceProxyProvider _serviceProxyProvider;
        private readonly IServiceRouteProvider _serviceRouteProvider;
        private readonly IAuthorizationServerProvider _authorizationServerProvider;

     
        public ServicesController(IServiceProxyProvider serviceProxyProvider, 
            IServiceRouteProvider serviceRouteProvider,
            IAuthorizationServerProvider authorizationServerProvider)
        {
            _serviceProxyProvider = serviceProxyProvider;
            _serviceRouteProvider = serviceRouteProvider;
            _authorizationServerProvider = authorizationServerProvider;
        }

        public async Task<ServiceResult<object>> Path([FromServices] IServicePartProvider servicePartProvider,
            string path, [FromBody] Dictionary<string, object> model)
        {
            string serviceKey = this.Request.Query["servicekey"];
            if (model == null)
            {
                model = new Dictionary<string, object>();
            }

            foreach (string n in this.Request.Query.Keys)
            {
                model[n] = this.Request.Query[n].ToString();
            }

            ServiceResult<object> result = ServiceResult<object>.Create(false, null);
            path = path.ToLower() == GateWayAppConfig.TokenEndpointPath.ToLower()
                ? GateWayAppConfig.AuthorizationRoutePath : path.ToLower();
            if (servicePartProvider.IsPart(path))
            {
                result = ServiceResult<object>.Create(true, await servicePartProvider.Merge(path, model));
                result.StatusCode = (int) CustomApiResultCode.HttpPortErr;
            }
            else if (OnAuthorization(path, model, ref result))
            {
                if (path == GateWayAppConfig.AuthorizationRoutePath)
                {
                    //为授权登录绑定客户端信息
                    var keyValue = model.FirstOrDefault();
                    dynamic instance = keyValue.Value;
                    if (keyValue.Value == null)
                    {
                        instance = new ExpandoObject();
                    }
                    instance.RequestInfo = JsonConvert.SerializeObject(new
                    {
                        IpAddress = HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault() ??
                                    HttpContext.Connection.RemoteIpAddress.ToString()
                    });
                    model.Remove(keyValue.Key);
                    model.Add(keyValue.Key, JsonConvert.DeserializeObject(JsonConvert.SerializeObject(instance)));
                }
                var proxyRpcResult = await _serviceProxyProvider.Invoke<object>(model, path,
                    string.IsNullOrEmpty(serviceKey) ? null : serviceKey);
                if (proxyRpcResult != null)
                {
                    //由于messagepack对时间处理不友好,所以只能在这里进行一次json.net反序化
                    var proxyResult = JsonConvert.DeserializeObject<CustomApiResult>(proxyRpcResult.ToString());
                    result.IsSucceed = true;
                    result.StatusCode = proxyResult.code.GetValue();
                    result.Entity = proxyResult.data;
                    result.Message = proxyResult.message;
                }
                else
                {
                    if (path == GateWayAppConfig.AuthorizationRoutePath)
                    {
                        result.StatusCode = (int) CustomApiResultCode.AuthenticationInvalid;
                        result.Message = "授权登录失败,请稍后";
                    }
                    else
                    {
                        result.StatusCode = (int) CustomApiResultCode.HttpPortErr;
                        result.Message = "服务异常,请稍后再试";
                    }
                }
            }
            return result;
        }

        private bool OnAuthorization(string path, Dictionary<string, object> model,ref ServiceResult<object> result)
        {
            bool isSuccess = true;
            var route = _serviceRouteProvider.GetRouteByPath(path).Result;
            if (route.ServiceDescriptor.EnableAuthorization())
            {
                if(route.ServiceDescriptor.AuthType()== AuthorizationType.JWT.ToString())
                {
                    isSuccess = ValidateJwtAuthentication(route,model, path,ref result);
                }
                else
                {
                    isSuccess = ValidateAppSecretAuthentication(route, path, model, ref result);
                }
            }
            return isSuccess;
        }

        public bool ValidateJwtAuthentication(ServiceRoute route, Dictionary<string, object> model, string path,
            ref ServiceResult<object> result)
        {
            var author = HttpContext.Request.Headers["Authorization"];
            if (author.Count > 0)
            {
                var tokenModel = new Dictionary<string, object>();
                tokenModel.Add("input",
                    JsonConvert.DeserializeObject(JsonConvert.SerializeObject(new {Token = author[0], Path = path})));
                var tokenResult = _serviceProxyProvider
                    .Invoke<CustomApiResult>(tokenModel, "api/auth/AuthorizationCheck", "Auth").Result;
                if (tokenResult.code == CustomApiResultCode.Succ)
                {
                    result.IsSucceed = true;
                    var keyValue = model.FirstOrDefault();
                    dynamic instance = keyValue.Value;
                    if (keyValue.Value == null)
                    {
                        instance = new ExpandoObject();
                    }
                    instance.AuthInfo = tokenResult.data;
                    instance.RequestInfo = JsonConvert.SerializeObject(new
                    {
                        IpAddress = HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault() ??
                                    HttpContext.Connection.RemoteIpAddress.ToString()
                    });
                    instance.Token = author[0];
                    model.Remove(keyValue.Key);
                    model.Add(keyValue.Key, JsonConvert.DeserializeObject(JsonConvert.SerializeObject(instance)));
                }
                else
                {
                    if (tokenResult.code == CustomApiResultCode.AuthenticationInvalid)
                        result = new ServiceResult<object>
                        {
                            IsSucceed = false,
                            StatusCode = (int) CustomApiResultCode.AuthenticationInvalid,
                            Message = "认证凭据无效或已过期"
                        };
                    else if (tokenResult.code == CustomApiResultCode.AuthorizationInvalid)
                        result = new ServiceResult<object>
                        {
                            IsSucceed = false,
                            StatusCode = (int) CustomApiResultCode.AuthorizationInvalid,
                            Message = "权限不足"
                        };
                }
            }
            else
            {
                result = new ServiceResult<object>
                {
                    IsSucceed = false,
                    StatusCode = (int) CustomApiResultCode.AuthenticationInvalid,
                    Message = "无效认证凭据"
                };
            }
            return result.IsSucceed;
        }

        private bool ValidateAppSecretAuthentication(ServiceRoute route, string path,
            Dictionary<string, object> model, ref ServiceResult<object> result)
        {
            bool isSuccess = true;
            DateTime time;
            var author = HttpContext.Request.Headers["Authorization"];

            if (!string.IsNullOrEmpty(path) && model.ContainsKey("timeStamp") && author.Count > 0)
            {
                if (DateTime.TryParse(model["timeStamp"].ToString(), out time))
                {
                    var seconds = (DateTime.Now - time).TotalSeconds;
                    if (seconds <= 3560 && seconds >= 0)
                    {
                        if (EncryptHelper.GetMd5($"{route.ServiceDescriptor.Token}{time.ToString("yyyy-MM-dd hh:mm:ss") }") != author.ToString())
                        {
                            result = new ServiceResult<object> { IsSucceed = false, StatusCode = (int)ServiceStatusCode.AuthorizationFailed, Message = "Invalid authentication credentials" };
                            isSuccess = false;
                        }
                    }
                    else
                    {
                        result = new ServiceResult<object> { IsSucceed = false, StatusCode = (int)ServiceStatusCode.AuthorizationFailed, Message = "Invalid authentication credentials" };
                        isSuccess = false;
                    }
                }
                else
                {
                    result = new ServiceResult<object> { IsSucceed = false, StatusCode = (int)ServiceStatusCode.AuthorizationFailed, Message = "Invalid authentication credentials" };
                    isSuccess = false;
                }
            }
            else
            {
                result = new ServiceResult<object> { IsSucceed = false, StatusCode = (int)ServiceStatusCode.RequestError, Message = "Request error" };
                isSuccess = false;
            }
            return isSuccess;
        }
    }
}
